<?php
use lib\Tips;
use Exception;
use Throwable;
use ReflectionClass;
use ReflectionMethod;

/**
 * Class App
 * 
 * 应用程序核心类，负责初始化路由配置、处理请求 URI，并根据路由规则调用相应的控制器和方法。
 * 接收方法 附带三个参数  分别为 get  post postjson 可接收可空 
 */
class App
{
    protected $routesConfig; // 保存路由配置

    /**
     * App constructor.
     * 
     * 构造函数用于加载路由配置文件。
     * 路由配置文件为 JSON 格式，如若配置文件不存在，则抛出异常以确保应用程序的正常运行。
     * 
     * @throws Exception 当路由配置文件未找到时抛出异常
     */
    public function __construct()
    {
        // 加载路由配置
        $routesFile = SYSTEM_ROOT . 'routes/routes.json';
        if (file_exists($routesFile)) {
            $routesJson = file_get_contents($routesFile);
            $this->routesConfig = json_decode($routesJson, true);
        } else {
            Tips::error("路由配置文件未找到!");
        }
    }

    /**
     * 运行应用，处理请求并执行相应的控制器与方法。
     * 
     * 此方法通过解析 URI，确定应调用的控制器及其方法。
     * 如果 URI 匹配路由规则，相关控制器将被实例化并调用其方法。
     * 
     * @param string $namespace 可选命名空间，提供模块化支持，默认情况下与 URI 第一部分相同。
     * 
     * @return void
     */
    public function run($namespace = '' , $parts = '')
    {
        try {
            // 获取请求的 URI
            $requestUri = (!empty($namespace) && !empty($parts)) ?  '/' . $namespace. '/'.$parts  : $_SERVER['REQUEST_URI'];
            $uri = parse_url($requestUri, PHP_URL_PATH);
            $uri = ltrim($uri, '/');
            $parts = explode('/', $uri); // 解析 URI
            
            
            // 选择是否禁用 admin 默认目录
            if(adminststus && ($parts[0] === 'admin' || $parts[0] === 'admin.php')){
                Tips::error($parts[0] . "路由被禁用 请使用安全入口进入");
            }
    
            // 如果 $namespace 存在，将 URI 的第一个部分替换为 $namespace
            if (!empty($namespace)) {
               
                $parts[0] = $namespace; // 替换第一个部分为 $namespace
                 
                $uri = implode('/', $parts);
                
            }
    
            // 如果 $parts 数组长度为 1，默认将第二个部分替换为 'index'
            // var_dump($requestUri);
            // exit;
            if(count($parts) == 1 ){
                $parts[1] = 'index';
                $uri = implode('/', $parts);
                if($parts[0] != 'api' && !empty($parts[0])){
                    adminhost();
                    exit;
                }
                
            }
            if(empty($parts[1])){
                $parts[1] = 'index';
                $uri = implode('/', $parts);
            }
            
            // 如果 URI 为空，加载默认控制器
            if (empty($uri) && $namespace) {
                $this->execute("$namespace/IndexController", 'index');
                return;
            }
            
            if($uri == '/index'){
                $this->execute("index/IndexController", 'index');
                return;
            }
            
            
            
            $routeFound = false;
            // 遍历路由规则
            foreach ($this->routesConfig['routes'] as $route) {
               
                if (preg_match('#' . $route['pattern'] . '#', $uri, $matches)) {
                     
                    $routeFound = true;
                    // 获取目标控制器和方法
                    $target = $route['target'];
                    $params = [];
    
                    // 替换 target 中的动态变量
                    $controller = preg_replace_callback('#\$(\d+)#', function ($m) use ($matches) {
                        return $matches[$m[1]];
                    }, $target);
    
                    // 处理参数
                    if (isset($route['params'])) {
                        foreach ($route['params'] as $key => $value) {
                            
                                $params[$key] = preg_replace_callback('#\$(\d+)#', function ($m) use ($matches) {
                                    return $matches[$m[1]];
                                }, $value);
                        }
                    }
                    
                    if (isset($route['allowed_s_values']) && !in_array($params['s'], $route['allowed_s_values'])) {
                        http_response_code(403); 
                        include ROOT . '404.php';
                        return;
                    }
                    if (isset($route['refuse_s_values']) && in_array($params['s'], $route['refuse_s_values'])) {
                        http_response_code(403); 
                        include ROOT . '404.php';
                        return;
                    }
                    // 解析控制器和方法并执行
                    $this->execute($controller, $params['s'] ?? 'index', $params);
                    return;
                }
                
            }
            
            if (!$routeFound) {
                
                http_response_code(403);
                include ROOT . '404.php';
                exit;
            }
        }catch (Throwable $e) {
            http_response_code(500);
            
            // 根据错误类型进行不同处理
            if ($e instanceof ParseError) {
                $errorType = "语法解析错误";
            } elseif ($e instanceof TypeError) {
                $errorType = "类型错误";
            } elseif ($e instanceof Error) {
                $errorType = "运行时错误";
            } else {
                $errorType = "未知错误";
            }
            
            $errorDetails = [
                'type' => $errorType,
                'message' => $e->getMessage(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => explode("\n", $e->getTraceAsString())
            ];
            // 根据调试模式显示错误
            if(defined('debug') && debug) {
                // 记录详细错误日志
                error_log(json_encode($errorDetails, JSON_PRETTY_PRINT));
                $errorMessage = "";
                $errorMessage .= "<strong>类型：</strong> " . htmlspecialchars($errorDetails['type']) . "<br>";
                $errorMessage .= "<strong>消息：</strong> " . htmlspecialchars($errorDetails['message']) . "<br>";
                $errorMessage .= "<strong>文件：</strong> " . htmlspecialchars($errorDetails['file']) . "<br>";
                $errorMessage .= "<strong>行号：</strong> " . $errorDetails['line'] . "<br>";
                $errorMessage .= "<h3>堆栈跟踪：</h3>";
                $errorMessage .= "<pre>" . htmlspecialchars(implode("\n", $errorDetails['trace'])) . "</pre>";
                Tips::error($errorMessage, $errorType);
            } else {
                Tips::error("系统发生错误，请稍后重试",$errorType);
            }
        }
        // 如果没有匹配到路由，返回 404
        http_response_code(403);
        include ROOT . '404.php';
        exit;
    }

    /**
     * 执行指定控制器的方法。
     * 
     * 此方法负责加载控制器文件，检查控制器和方法是否存在，并最终调用该方法。
     * 在调用方法时，将 GET、POST 和其他输入参数传递给控制器。
     * 
     * @param string $controller 控制器的完整类名，支持以斜杠或反斜杠表示的命名空间。
     * @param string $method 控制器的方法名。
     * @param array $params 调用方法时传递的参数数组，包含来自请求的输入数据。
     * 
     * @return void
     */
    protected function execute($controller, $method, $params = [])
    {
        try {
            // 加载基础控制器
            require_once ROOT . 'includes/class/lib/BaseController.php';
    
            // 支持多命名空间路径解析
            $controllerPath = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, $controller);
            $controllerFile = ROOT . 'includes/class/XFCore/controllers/' . $controllerPath . '.php';
    
            if (!file_exists($controllerFile)) {
                throw new Exception("控制器文件未找到！", 404);
            }
    
            require_once $controllerFile; // 包含控制器文件
    
            // 转换为命名空间
            $className = "XFCore\\controllers\\" . str_replace(['/', '\\'], '\\', $controller);
            if (!class_exists($className)) {
                throw new Exception("控制器类 $controller 未定义！", 500);
            }
    
            $instance = new $className(); // 实例化控制器
    
            // 动态方法检查
            if (!method_exists($instance, $method)) {
                throw new Exception("方法 $method 在控制器中未定义！", 500);
            }
    
            // 准备参数
            unset($params['s']);
            $mergedParams = [
                array_merge($params, $this->getInputData('get')),
                $this->getInputData('post'),
                $this->getInputData('input')
            ];
    
            // 调用控制器方法
            return call_user_func_array([$instance, $method], $mergedParams);
    
        } catch (Exception $e) {
            $this->handleError($e);
        }
    }
    
    protected function getInputData($type)
    {
        switch ($type) {
            case 'get':
                return input("get");
            case 'post':
                return input("post");
            case 'input':
                return input("input") ?: [];
            default:
                return [];
        }
    }
    
    protected function handleError(Exception $e)
    {
       
        http_response_code(500);
        Tips::error($e->getMessage());
        // 可以添加日志记录
        if(debug)error_log($e->getMessage());
    }
}
